//CollisionsHandler.cpp

#include "CollisionsHandler.h"
#include "Rect.h"
#include "Circle.h"


// Handles collisions between a CIRCLE and another CIRCLE
void CollisionsHandler::handleCollisions(Circle* circ1, Circle* circ2)
{
	bool colX = false; // If a collision on the X axis
	bool colY = false; // If a collision on the Y axis
	int faster = 0;
	
	isCircleCollideWithBorder(circ1,colX,colY);
	if(colX)
		circ1->flagChangeX();
	if(colY)
		circ1->flagChangeY();

	colX=colY=false;

	isCircleCollideWithBorder(circ2,colX,colY);
	if(colX)
		circ2->flagChangeX();
	if(colY)
		circ2->flagChangeY();

	colX=colY=false;


	isCircToCircCollisionXandY(circ1,circ2,colX,colY); // Finds if there are collisions and on which axises

		if(colX) // If circles collided on the X axis
		{
			// both in the SAME direction
			if ( (circ1->getShiftHor() > 0 && circ2->getShiftHor() > 0) || (circ1->getShiftHor() < 0 && circ2->getShiftHor() < 0) )
			{
				faster = whoIsFasterHor(circ1,circ2);
				switch(faster)
				{
					case 0:
						circ1->flagChangeX();
						break;
					case 1:		
						circ1->flagChangeX();
						break;
					case 2:
						circ2->flagChangeX();
						break;
				}
			}
			else // if in other directions
			{
				circ1->flagChangeX();
				circ2->flagChangeX();
			}
		} // End of IF Collision X

		if(colY) // If collide on the Y axis
		{
			// both in the SAME direction
			if ( (circ1->getShiftVer() > 0 && circ2->getShiftVer() > 0) || (circ1->getShiftVer() < 0 && circ2->getShiftVer() < 0) )
			{
				faster = whoIsFasterVer(circ1,circ2);
				switch(faster)
				{
					case 0:
						circ1->flagChangeY();
						break;
					case 1:		
						circ1->flagChangeY();
						break;
					case 2:
						circ2->flagChangeY();
						break;
				}
			}
			else // other directions
			{
				circ1->flagChangeY();
				circ2->flagChangeY();
			}
		} // End of IF Collision Y
}

// // Handles collisions between CIRCLE and RECT
void CollisionsHandler::handleCollisions(Circle* circ, Rect* rect)
{
	bool colX = false; // If a collision on the X axis
	bool colY = false; // If a collision on the Y axis
	int faster = 0;

	//////////////////////////////
	// Checks collisions with borders -- needs to be improved
	isCircleCollideWithBorder(circ,colX,colY);
	if(colX)
		circ->flagChangeX();
	if(colY)
		circ->flagChangeY();

	colX=colY=false;

	isRecCollideWithBorder(rect,colX,colY);
	if(colX)
		rect->flagChangeX();
	if(colY)
		rect->flagChangeY();

	colX=colY=false;
//////////////////////////////


	isCircToRecCollisionXandY(circ,rect,colX,colY); // Finds if there are collisions and on which axises

		if(colX) // If circle and rectangle collided on the X axis
		{
			// both in the SAME direction
			if ( (circ->getShiftHor() > 0 && rect->getShiftHor() > 0) || (circ->getShiftHor() < 0 && rect->getShiftHor() < 0) )
			{
				faster = whoIsFasterHor(circ,rect);
				switch(faster)
				{
					case 0:
						rect->flagChangeX();
					case 1:		
						circ->flagChangeX();
						break;
					case 2:
						rect->flagChangeX();
						break;
				}
			}
			else // if in other directions
			{
				circ->flagChangeX();
				rect->flagChangeX();
			}
		} // End of IF Collision X

		if(colY) // If collide on the Y axis
		{
			// both in the SAME direction
			if ( (circ->getShiftVer() > 0 && rect->getShiftVer() > 0) || (circ->getShiftVer() < 0 && rect->getShiftVer() < 0) )
			{
				faster = whoIsFasterVer(circ,rect);
				switch(faster)
				{
					case 0:
						rect->flagChangeY();
					case 1:		
						circ->flagChangeY();
						break;
					case 2:
						rect->flagChangeY();
						break;
				}
			}
			else // other directions
			{
				circ->flagChangeY();
				rect->flagChangeY();
			}
		} // End of IF Collision Y
}

// Handles collisions between RECT and CIRCLE --- sends to same function that handles circle and rect
void CollisionsHandler::handleCollisions(Rect* rect, Circle* circ)
{
	handleCollisions(circ,rect);
}

// Handles collision between RECT and other RECT
void CollisionsHandler::handleCollisions(Rect *rect1,Rect *rect2)
{
	bool colX = false; // If a collision on the X axis
	bool colY = false; // If a collision on the Y axis
	int faster = 0;
	int isRecInRec = 0;


//////////////////////////////
	// Checks collisions with borders -- needs to be improved
	isRecCollideWithBorder(rect1,colX,colY);
	if(colX)
		rect1->flagChangeX();
	if(colY)
		rect1->flagChangeY();

	colX=colY=false;

	isRecCollideWithBorder(rect2,colX,colY);
	if(colX)
		rect2->flagChangeX();
	if(colY)
		rect2->flagChangeY();

	colX=colY=false;
//////////////////////////////


	isRecToRecCollisionXandY(rect1,rect2, colX, colY);
	
		if(colX) // If rects collide on the X axis
		{
			// both in the SAME direction
			if ( (rect1->getShiftHor() > 0 && rect2->getShiftHor() > 0) || (rect1->getShiftHor() < 0 && rect2->getShiftHor() < 0) )
			{
				faster = whoIsFasterHor(rect1,rect2);
				switch(faster)
				{
					case 1:		
						rect1->flagChangeX();
						break;
					case 2:
						rect2->flagChangeX();
						break;
				}
			}
			else // other directions
			{
				rect1->flagChangeX();
				rect2->flagChangeX();
			}
		} // End of IF Collision X

		if(colY) // If collide on the Y axis
		{
			// both in the SAME direction
			if ( (rect1->getShiftVer() > 0 && rect2->getShiftVer() > 0) || (rect1->getShiftVer() < 0 && rect2->getShiftVer() < 0) )
			{
				faster = whoIsFasterVer(rect1,rect2);
				switch(faster)
				{
					case 1:		
						rect1->flagChangeY();
						break;
					case 2:
						rect2->flagChangeY();
						break;
				}
			}
			else // other directions
			{
				rect1->flagChangeY();
				rect2->flagChangeY();
			}
		} // End of IF Collision Y
}

// returns 1 if rec1 is in rec2, 2 if rec2 is in rec1, and 3 if neither.
int CollisionsHandler::isRectangleInRectangle(const Rect *rect1,const Rect *rect2)
{
	int rect1X = rect1->getXPoint(); // location of X
	int rect1Y = rect1->getYPoint(); // location of Y
	int rect1hor = rect1->getHor(); // horizontal
	int rect1ver = rect1->getVer(); // vertical

	int rect2X = rect2->getXPoint();
	int rect2Y = rect2->getYPoint();
	int rect2hor = rect2->getHor(); // horizontal
	int rect2ver = rect2->getVer(); // vertical


	if( (rect1X <= rect2X) && (rect1Y <= rect2Y) && // UL Point
		(rect1X + rect1hor >= rect2X + rect2hor) && (rect1Y <= rect2Y) && // UR Point
		(rect1X <= rect2X) && (rect1Y + rect1ver >= rect2Y + rect2ver) && // BL Point
		(rect1X + rect1hor >= rect2X + rect2hor) && (rect1Y + rect1ver >= rect2Y + rect2ver)// BR Point
		)
		return 2;

	if( (rect2X <= rect1X) && (rect2Y <= rect1Y) && // UL Point
		(rect2X + rect2hor >= rect1X + rect1hor) && (rect2Y <= rect1Y) && // UR Point
		(rect2X <= rect1X) && (rect2Y + rect2ver >= rect1Y + rect1ver) && // BL Point
		(rect2X + rect2hor >= rect1X + rect1hor) && (rect2Y + rect2ver >= rect1Y + rect1ver)// BR Point
		)
		return 1;

	return 3;
}

// Receives two rectangles and tells if they have collided on the X axis or Y axis (by reference)
void CollisionsHandler::isRecToRecCollisionXandY(const Rect* rect1,const Rect* rect2, bool& colX , bool& colY)
{
	colX = false; // Just in case :-)
	colY = false;
	int rect1X = rect1->getXPoint(); // location of X
	int rect1Y = rect1->getYPoint(); // location of Y
	int rect1hor = rect1->getHor(); // horizontal
	int rect1ver = rect1->getVer(); // vertical

	int rect2X = rect2->getXPoint();
	int rect2Y = rect2->getYPoint();
	int rect2hor = rect2->getHor(); // horizontal
	int rect2ver = rect2->getVer(); // vertical

	if( (rect1X == rect2X) || (rect2X == rect1X + rect1hor) || (rect1X + rect1hor == rect2X + rect2hor) || (rect1X == rect2X + rect2hor))
	{
		if( ((rect1Y <= rect2Y) && (rect1Y + rect1ver >= rect2Y)) || ((rect1Y <= rect2Y + rect2ver) && (rect1Y + rect1ver >= rect2Y + rect2ver)) 
			|| ( (rect1Y >= rect2Y) && (rect1Y+rect1ver <= rect2Y+rect2ver)))
			colX = true;

	}
	if( (rect1Y == rect2Y) || (rect2Y == rect1Y + rect1ver) || (rect1Y + rect1ver == rect2Y + rect2ver) || (rect1Y == rect2Y + rect2ver))
	{
		if( ((rect1X <= rect2X) && (rect1X + rect1hor >= rect2X)) || ((rect1X <= rect2X + rect2hor) && (rect1X + rect1hor >= rect2X + rect2hor)) 
			|| ( (rect1X >= rect2X) && (rect1X+rect1hor <= rect2X+rect2hor)))
			colY = true;

	}

}

// Finds if rectangle collides with the screen borders. Returns true if one of the axis hits, false otherwise.
bool CollisionsHandler::isRecCollideWithBorder(const Rect* rect, bool& colX, bool& colY)
{
	int recX = rect->getXPoint(); // location of X
	int recY = rect->getYPoint(); // location of Y
	int recHor = rect->getHor(); // horizontal
	int recVer = rect->getVer(); // vertical
	int shiftX = (int)rect->getShiftHor();
	int shiftY = (int)rect->getShiftVer();
	colX = colY = false;
	float directionX = rect->getShiftHor();
	float directionY = rect->getShiftVer();

	if ( ( (recX == 0) && (directionX < 0) ) || ( ( recX + recHor == (SCR_HOR-1) ) && (directionX > 0) ) )

		colX = true; // Collides on X axis
	if ( ((recY == 0) && (directionY < 0) ) || ((recY + recVer == (SCR_VER-1)) && (directionY > 0) ) )
		colY = true; // collides on Y axis
	
	return (colX || colY);
}

// Horizontal speed comparison: 0 - both rects same speed, 1 - rect 1 is FASTER, 2 - rect 2 is FASTER
int CollisionsHandler::whoIsFasterHor(const Shape* shape1, const Shape* shape2)
{
	int flag = 0;

	if (shape1->getShiftHor() > shape2->getShiftHor())
		flag = 1;
	else if (shape1->getShiftHor() < shape2->getShiftHor())
		flag = 2;

	return flag;
}


// Vertical speed comparison: 0 - both rects same speed, 1 - rect 1 is FASTER, 2 - rect 2 is FASTER
int CollisionsHandler::whoIsFasterVer(const Shape* shape1, const Shape* shape2)
{
	int flag = 0;

	if (shape1->getShiftVer() > shape2->getShiftVer())
		flag = 1;
	else if (shape1->getShiftVer() < shape2->getShiftVer())
		flag = 2;

	return flag;
}

// Receives two circles and tells if they have collided on the X axis or Y axis, or both! (by reference)
void CollisionsHandler::isCircToCircCollisionXandY(const Circle* circ1,const Circle* circ2, bool& colX , bool& colY)
{
	int distanceCenters = 0;
	int radiusCirc1 = circ1->getRad();
	int radiusCirc2 = circ2->getRad();
	float deltaX = (float) abs(circ1->getXPoint() - circ2->getXPoint());
	float deltaY = (float) abs(circ1->getYPoint() - circ2->getYPoint());

	colX = colY = false;

	distanceCenters = circ1->getPoint().distanceToOtherPoint(circ2->getPoint()); // distance between centers

	if ( isCircleInCircle(*circ1 , *circ2) ) // Circle 1 is In Circle 2
	{
		if( circ1->getPoint().distanceToOtherPoint(circ2->getPoint()) + circ2->getRad() == circ1->getRad() ) // Kaboom!!!!
		{
			if (deltaX > deltaY)
			{
				colX = true;
			}
			if (deltaX < deltaY)
			{
				colY = true;
			}
			if (deltaX == deltaY)
				colX = colY = true;
		}

	} // End of Circle in Circle

	// Now check for external collisions
	else if( distanceCenters == radiusCirc1 + radiusCirc2) // KaBoOm!!!! ;-)
	{
			if (deltaX > deltaY)
			{
				colX = true;
			}
			if (deltaX < deltaY)
			{
				colY = true;
			}
			if (deltaX == deltaY)
				colX = colY = true;
	}

}

void CollisionsHandler::whichQuarterInCircle(const Point& point, const Circle& circle, bool& colX , bool& colY)
{
	int x = point.getX();
	int y = point.getY();
	

	int cenX = circle.getXPoint();
	int cenY = circle.getYPoint();
	int radius = circle.getRad();
	colX = colY = false;
	
	// Initilize key points in circle
	Point up , left , right , down;
	up.set( cenX, cenY-radius);
	left.set( cenX - radius, cenY);
	right.set( cenX + radius, cenY);
	down.set( cenX, cenY + radius);


	if( x > cenX) // Right Side
	{
		if ( y > cenY) // Top Right Quarter
		{
			if ( x == (int)(cenX + sqrt((float)(radius*radius)/2)) &&
				y == (int)(cenY - sqrt((float)(radius*radius)/2)))
			colX = colY = true; // Collision exactly in middle of quarter
			else if ( up.distanceToOtherPoint(point) < right.distanceToOtherPoint(point) )
				colY = true; // Closer to up
				else
				colX = true; // Closer to right
		}
		else if ( y < cenY) // Bottom Right Quarter
		{
			if ( x == (int)(cenX + sqrt((float)(radius*radius)/2)) &&
				y == (int)(cenY + sqrt((float)(radius*radius)/2)))
			colX = colY = true; // Collision exactly in middle of quarter
			else if ( down.distanceToOtherPoint(point) < right.distanceToOtherPoint(point) )
				colY = true; // Closer to down
				else
				colX = true; // Closer to right
		}
		else // y == cenY
		{
			colX = true;
		}

	}
	else if ( x < cenX) // Left Side
	{
		if ( y > cenY) // Top Left Quarter
		{
			if ( x == (int)(cenX - sqrt((float)(radius*radius)/2)) &&
				y == (int)(cenY - sqrt((float)(radius*radius)/2)))
			colX = colY = true; // Collision exactly in middle of quarter
			else if ( up.distanceToOtherPoint(point) < left.distanceToOtherPoint(point) )
				colY = true;
				else
				colX = true;
		}
		else if ( y < cenY) // Bottom Left Quarter
		{
			if ( x == (int)(cenX - sqrt((float)(radius*radius)/2)) &&
				y == (int)(cenY + sqrt((float)(radius*radius)/2)))
			colX = colY = true; // Collision exactly in middle of quarter
			else if ( down.distanceToOtherPoint(point) < left.distanceToOtherPoint(point) )
				colY = true;
				else
				colX = true;
		}
		else // y == cenY
		{
			colX = true;
		}
	}
	else // if ( x == cenX)
	{
		colY = true;
	}
}

// Checks if circle 1 is in circle 2. Returns: False - NO, True - YES
bool CollisionsHandler::isCircleInCircle(const Circle& circle1, const Circle& circle2)
{
	float deltaX = (float) abs(circle1.getXPoint() - circle2.getXPoint());
	float deltaY = (float) abs(circle1.getYPoint() - circle2.getYPoint());
	float radius = (float) circle1.getRad();

	if ( pow(deltaX,2) + pow(deltaY,2) < pow(radius,2) )
	{
		return true;
	}
	else return false;
}

// Receives a circle and returns in colX and colY whether the Circle collides with any borders.
bool CollisionsHandler::isCircleCollideWithBorder(const Circle* circ, bool& colX, bool& colY)
{
	int x = circ->getXPoint();
	int y = circ->getYPoint();
	float shiftX = circ->getShiftHor();
	float shiftY = circ->getShiftVer();
	int radius = circ->getRad();


	if(  (( (x - radius) == -1) && (shiftX < 0) ) || ( ( (x + radius) == (SCR_HOR-1) ) && (shiftX > 0) ) )  
				colX = true; // Collides with X Axis
	if ( (( (y - radius) == -1) && (shiftY < 0) ) || ( ( (y + radius+1) == (SCR_VER-1) ) && (shiftY > 0) ) )
				colY = true; // Collides with Y Axis

	return (colX || colY); // If collided
}


// Receives a rectangle and a circle, and tells if they have collided on the X axis or Y axis (by reference)
void CollisionsHandler::isCircToRecCollisionXandY(const Circle* circ,const Rect* rect, bool& colX , bool& colY)
{
	
	colX = false; // If a collision on the X axis
	colY = false; // If a collision on the Y axis
	int faster = 0;
	
	Point rectPoint = rect->getPoint();
	Point circPoint = circ->getPoint();
	int circRad = circ->getRad();
	int rectHor = rect->getHor();
	int rectVer = rect->getVer();

	int deltaLeft = abs(rectPoint.getX() - circPoint.getX()); // Distance from left side to center X
	int deltaRight = abs( rectPoint.getX() + rectHor - circPoint.getX()); // Distance from left side to center X
	int deltaUp = abs(rectPoint.getY() - circPoint.getY()); // Distance from left side to center X
	int deltaDown = abs(rectPoint.getY()+ rectVer - circPoint.getY()); // Distance from left side to center X
	
	// Two circles collide on X axis
	if ( ( deltaRight <= circRad ) || (deltaLeft <= circRad) )
		if (( deltaDown >= circPoint.getY() ) && ( deltaUp <= circPoint.getY() ) )
			colX = true;

	// Two circles collide on Y axis
	if ( ( deltaDown <= circRad ) || ( deltaUp <= circRad ) )
		if ( ( deltaRight >= circPoint.getX() ) && (deltaLeft <= circPoint.getX() ) )
			colY = true;
}